<?php
/**
 * Created by PhpStorm
 * User: Jhinwy
 * Date: 4/18/24
 * Time: 6:46 PM
 */

namespace app\common\service\media;

use app\common\model\media\Media;
use app\common\model\media\MediaAccount;
use app\common\model\media\MediaAuth;
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
use think\facade\Cache;
use think\facade\Log;

class KsService
{
    const REDIS_MEDIA_KS_TOKEN     = 'redis_media_ks_token_';
    const REDIS_MEDIA_KS_REF_TOKEN = 'redis_media_ks_ref_token_';

    protected $config;
    protected $appId;
    protected $secret;
    protected $scope;


    public function __construct($authId)
    {
        $mediaAuth    = MediaAuth::where('id', $authId)->findOrEmpty();
        $this->config = config('media.ks') ?? [];
        $this->appId  = $mediaAuth->app_id ?? '';
        $this->secret = $mediaAuth->secret ?? '';
        $this->scope  = $mediaAuth->scope ?? '';
    }


    /**
     * 获取快手授权链接
     *
     * @return string
     */
    public function getEmpowerUrl($state = 0)
    {
        $redirect_uri = env('app.host') . $this->config['redirect_uri'];
        return sprintf(
            $this->config['empower_url'] ?? '',
            $this->appId,
            urlencode($this->getScope()),
            urlencode($redirect_uri),
            $state,
            $this->config['oauth_type'] ?? ''
        );
    }


    /**
     * 获取scope
     *
     * @return string
     */
    protected function getScope()
    {
        return '["' . str_replace([',', '，'], '","', $this->scope) . '"]';
    }


    /**
     * 获取token
     *
     * @param string $authCode
     * @return array|bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getToken(string $authCode = '')
    {
        try {
            $headers = [
                'Content-Type' => 'application/json',
            ];
            $fields  = [
                'app_id'    => $this->appId,
                'secret'    => $this->secret,
                'auth_code' => $authCode
            ];
            Log::write('请求快手授权Token==>' . json_encode($fields), 'info');
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['token_api'] ?? '',
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            list($advertiserId, $accessToken) = $this->cacheToken($data);
            if (!$accessToken) {

                throw new \Exception('无效的Token');
            }

            return [$advertiserId, $accessToken];
        } catch (\Exception $exception) {
            Log::error('获取快手token失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 缓存token
     *
     * @param $data
     * @return array|bool
     */
    public function cacheToken($data)
    {
        Log::write('缓存快手授权Token==>' . json_encode($data), 'info');
        $data                 = $data['data'] ?? [];
        $accessToken          = $data['access_token'] ?? '';
        $accessTokenExpiresIn = $data['access_token_expires_in'] ?? 0;
        $refToken             = $data['refresh_token'] ?? 0;
        $refTokenIn           = $data['refresh_token_expires_in'] ?? 0;
        if (!$advertiserId = $data['advertiser_id'] ?? 0) {
            $advertiserId = $data['user_id'] ?? 0;
        }

        if (!$accessToken || !$accessTokenExpiresIn || !$refToken || !$refTokenIn) {

            return false;
        }

        // 缓存巨量Token
        Cache::set(self::REDIS_MEDIA_KS_TOKEN . $advertiserId, $accessToken, $accessTokenExpiresIn);
        Cache::set(self::REDIS_MEDIA_KS_REF_TOKEN . $advertiserId, $refToken, $refTokenIn);

        return [$advertiserId, $accessToken];
    }


    /**
     * 获取缓存token
     *
     * @return bool|mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getCacheToken($advertiserId)
    {
        if ($accessToken = Cache::get(self::REDIS_MEDIA_KS_TOKEN . $advertiserId)) {

            return $accessToken;
        }
        // token过期刷新快手token
        if ($refToken = Cache::get(self::REDIS_MEDIA_KS_REF_TOKEN . $advertiserId)) {
            if ($accessToken = $this->refToken($refToken)) {

                return $accessToken;
            }
        }

        return false;
    }


    /**
     * 刷新快手token
     *
     * @param $refToken
     * @return bool
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function refToken($refToken)
    {
        try {
            $headers  = [
                'Content-Type' => 'application/json',
            ];
            $fields   = [
                'app_id'        => $this->appId,
                'secret'        => $this->secret,
                'refresh_token' => $refToken
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['ref_token_api'] ?? '',
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            list($advertiserId, $accessToken) = $this->cacheToken($data);
            if (!$accessToken) {

                throw new \Exception('无效的Token');
            }

            return $accessToken;
        } catch (\Exception $exception) {
            Log::error('刷新快手token失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取广告主
     *
     * @param $advertiserId
     * @param $token
     * @param $state
     * @return bool|int
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getAdvertiser($authId, $advertiserId, $token)
    {
        try {
            $headers  = [
                'Content-Type' => 'application/json',
                'Access-Token' => $token
            ];
            $fields   = [
                'advertiser_id' => $advertiserId,
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['advertiser_api'] ?? '',
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            Log::info('【快手广告主信息】' . $contents);
            $data = json_decode($contents, true);
            $data = $data['data'] ?? [];
            if (!$details = $data['details'] ?? []) {
                throw new \Exception('广告主信息不存在');
            }

            $insertData = [];
            foreach ($details as $detail) {
                if (MediaAccount::where('advertiser_id', $detail['advertiser_id'] ?? 0)->where('media_id', Media::SOURCE_KS)->findOrEmpty()->isEmpty()) {
                    $insertData[] = [
                        'media_auth_id'      => $authId,
                        'auth_advertiser_id' => $advertiserId,
                        'advertiser_id'      => $detail['advertiser_id'] ?? 0,
                        'advertiser_name'    => $detail['advertiser_name'] ?? '',
                        'company'            => $detail['corporation_name'] ?? '',
                        'media_id'           => Media::SOURCE_KS,
                        'create_time'        => time(),
                        'update_time'        => time()
                    ];
                }
            }

            return MediaAccount::insertAll($insertData);
        } catch (\Exception $exception) {
            Log::error('获取快手授权广告主失败：' . $exception->getMessage());

            return false;
        }
    }


    /**
     * 获取广告主成本
     *
     * @param $authId
     * @param $advertiserId
     * @param $linkId
     * @param $date
     * @return int
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getAdvertiserReport($authId, $advertiserId, $date)
    {
        try {
            if (!$token = $this->getCacheToken($authId)) {

                return 0;
            }
            $headers  = [
                'Content-Type' => 'application/json',
                'Access-Token' => $token
            ];
            $fields   = [
                'advertiser_id' => $advertiserId,
                'start_date'    => $date,
                'end_date'      => $date,
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['report_api'],
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            $data     = $data['data'] ?? [];
            if (!$details = $data['details'] ?? []) {

                return 0;
            }
            $costMoney = 0;
            foreach ($details as $detail) {
                $costMoney += $detail['charge'] ?? 0;
            }

            if ($costMoney) {

                return $costMoney;
            }

            return 0;
        } catch (\Exception $exception) {
            Log::error('获取广告主成本失败：' . $exception->getMessage());

            return 0;
        }
    }


    /**
     * 获取广告主单元报告(广告组)
     *
     * @param $authId
     * @param $advertiserId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getUnitReport($authId, $advertiserId, $unitIds = [], $date = '')
    {
        try {

            if (!$token = $this->getCacheToken($authId)) {

                return [];
            }
            $headers  = [
                'Content-Type' => 'application/json',
                'Access-Token' => $token
            ];
            $fields   = [
                'advertiser_id' => $advertiserId,
                'unit_ids'      => $unitIds,
                'start_date'    => $date,
                'end_date'      => $date,
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['unit_report_api'],
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            $data     = $data['data'] ?? [];
            if (!$details = $data['details'] ?? []) {

                return [];
            }
            $result = [
                'charge'        => 0,
                'show'          => 0,
                'photo_click'   => 0,
                'play_3s_ratio' => 0,
                'block'         => 0,
                'report'        => 0,
                'negative'      => 0,
            ];
            foreach ($details as $detail) {
                $result['charge']        += $detail['charge'] ?? 0;
                $result['show']          += $detail['show'] ?? 0;
                $result['photo_click']   += $detail['photo_click'] ?? 0;
                $result['play_3s_ratio'] += $detail['play_3s_ratio'] ?? 0;
                $result['block']         += $detail['block'] ?? 0;
                $result['report']        += $detail['report'] ?? 0;
                $result['negative']      += $detail['negative'] ?? 0;
            }

            return $result;
        } catch (\Exception $exception) {
            Log::error('获取快手广告组报告失败：' . $exception->getMessage());

            return [];
        }
    }


    /**
     * 获取广告主创意
     *
     * @param $authId
     * @param $advertiserId
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getCreative($authId, $advertiserId, $date = '')
    {
        try {

            if (!$token = $this->getCacheToken($authId)) {

                return [];
            }
            $headers  = [
                'Content-Type' => 'application/json',
                'Access-Token' => $token
            ];
            $fields   = [
                'advertiser_id'    => $advertiserId,
                'start_date'    => $date,
                'end_date'      => $date,
                'page_size'        => 100,
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['creative_api'],
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            $data     = $data['data'] ?? [];
            if (!$details = $data['details'] ?? []) {

                return [];
            }
            $result = [];
            foreach ($details as $detail) {
                $photo_ids = array_merge($detail['vertical_photo_ids'] ?? [], $detail['horizontal_photo_ids'] ?? []);
                $result[]  = [
                    'unit_id'            => $detail['unit_id'] ?? '',
                    'captions'           => implode(',', $detail['captions'] ?? ''),
                    'cover_image_urls'   => implode(',', $detail['cover_image_urls'] ?? []),
                    'view_status_reason' => $detail['view_status_reason'] ?? '',
                    'package_name'       => $detail['package_name'] ?? '',
                    'photo_ids'          => $photo_ids
                ];
            }

            return $result;
        } catch (\Exception $exception) {
            Log::error('获取快手广告主创意失败：' . $exception->getMessage());

            return [];
        }
    }


    /**
     * 获取素材视频
     *
     * @param $authId
     * @param $advertiserId
     * @param $photoIds
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function getVideo($authId, $advertiserId, $photoIds)
    {
        try {

            if (!$token = $this->getCacheToken($authId)) {

                return [];
            }
            $headers  = [
                'Content-Type' => 'application/json',
                'Access-Token' => $token
            ];
            $fields   = [
                'advertiser_id' => $advertiserId,
                'photo_ids'     => $photoIds
            ];
            $client   = new Client();
            $response = $client->request(
                'POST',
                $this->config['video_api'],
                [
                    'headers'            => $headers,
                    'timeout'            => 60,
                    RequestOptions::JSON => $fields
                ]
            );
            if ($response->getStatusCode() !== 200) {
                throw new \Exception('请求失败');
            }
            $contents = $response->getBody()->getContents();
            $data     = json_decode($contents, true);
            if (!$data = $data['data'] ?? []) {

                return [];
            }
            $result = [];
            foreach ($data as $item) {
                $result[] = $item['url'] ?? '';
            }

            return $result;
        } catch (\Exception $exception) {
            Log::error('获取快手素材视频失败：' . $exception->getMessage());

            return [];
        }
    }
}
